package com.gict.antifraudback.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gict.antifraudback.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.http.*;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.util.EntityUtils;
import org.springframework.http.MediaType;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * HTTP请求封装，建议直接使用sdk的API
 */
@Slf4j
public class HttpHelper {

    /**
     * @param url
     * @return JSONObject
     * @throws Exception
     * @desc ：1.发起GET请求
     */
    public static JSONObject doGet(String url) throws Exception {

        //1.生成一个请求
        HttpGet httpGet = new HttpGet(url);
        //2.配置请求的属性
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(5000).setConnectTimeout(5000).build();//2000
        httpGet.setConfig(requestConfig);

        //3.发起请求，获取响应信息
        //3.1 创建httpClient
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        try {
            //3.2 发起请求，获取响应信息
            response = httpClient.execute(httpGet, new BasicHttpContext());

            //如果返回结果的code不等于200，说明出错了
            if (response.getStatusLine().getStatusCode() != 200) {

                log.error(
                        "HttpHelper doGet request url failed, http code={} url= {}",
                        response.getStatusLine().getStatusCode(), url);
                return null;
            }
            //4.解析请求结果
            HttpEntity entity = response.getEntity(); //reponse返回的数据在entity中
            if (entity != null) {
                String resultStr = EntityUtils.toString(entity, "utf-8"); //将数据转化为string格式
                log.info("GET  result={}", resultStr);
                JSONObject result = JSON.parseObject(resultStr); //将String转换为 JSONObject

                if (result.getInteger("errcode") == null) {
                    return result;
                } else if (0 == result.getInteger("errcode")) {
                    return result;
                } else {
                    log.info("request url={},return value={}", url, resultStr);
                    int errCode = result.getInteger("errcode");
                    String errMsg = result.getString("errmsg");
                    throw new Exception("error code:" + errCode + ", error message:" + errMsg);
                }
            }
        } catch (IOException e) {
            log.error("request url={}, exception, msg={}", url, e.getMessage());
            e.printStackTrace();
        } finally {
            if (response != null)
                try {
                    response.close(); //释放资源

                } catch (IOException e) {
                    e.printStackTrace();
                }
        }

        return null;
    }

    /**
     * post请求
     *
     * @param url
     * @param map
     * @return
     */
    public static String doPost(String url, Map<String,String> map) {
        HttpClient httpClient = new HttpClient();
        PostMethod postMethod = new PostMethod(url);

        postMethod.addRequestHeader("accept", "*/*");
        postMethod.addRequestHeader("connection", "Keep-Alive");
        //设置json格式传送
        postMethod.addRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        //必须设置下面这个Header
        postMethod.addRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36");
        //添加请求参数
        for (String paramName : map.keySet()) {
            postMethod.addParameter(paramName, map.get(paramName));
        }

        String res = "";
        try {
            int code = httpClient.executeMethod(postMethod);
            if (code == 200) {
                res = postMethod.getResponseBodyAsString();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return res;
    }

    /**
     * @param url     请求url
     * @param fileDir 下载路径
     * @return
     * @throws Exception File
     * @desc ： 4.下载文件 -get
     */
    public static File downloadMedia(String url, String fileDir) throws Exception {
        //1.生成一个请求
        HttpGet httpGet = new HttpGet(url);
        //2.配置请求属性
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(100000).setConnectTimeout(100000).build();
        httpGet.setConfig(requestConfig);

        //3.发起请求，获取响应信息
        //3.1 创建httpClient
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;

        //4.设置本地保存的文件
        //File file = new File(fileDir);
        File file = null;
        try {
            //5. 发起请求，获取响应信息
            response = httpClient.execute(httpGet, new BasicHttpContext());
            log.info("HttpStatus.SC_OK:{}", HttpStatus.SC_OK);
            log.info("response.getStatusLine().getStatusCode():{}", response.getStatusLine().getStatusCode());
            log.info("http-header:{}", JSON.toJSONString(response.getAllHeaders()));
            log.info("http-filename:{}", getFileName(response));

            //请求成功
            if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode()) {

                //6.取得请求内容
                HttpEntity entity = response.getEntity();
                String contentType = entity.getContentType().getValue();
                log.info("downloadMedia Content-Type:{}", contentType);
                if (contentType.indexOf("json") > 0) {
                    String retJson = EntityUtils.toString(entity, "UTF-8").trim();
                    log.error("downloadMedia error errormsg={}", retJson);
                }

                if (entity != null) {
                    //这里可以得到文件的类型 如image/jpg /zip /tiff 等等 但是发现并不是十分有效，有时明明后缀是.rar但是取到的是null，这点特别说明
                    //log.info(entity.getContentType());
                    //可以判断是否是文件数据流
                    log.info("entity.isStreaming()={}", entity.isStreaming());

                    //6.1 输出流
                    //6.1.1获取文件名，拼接文件路径
                    String fileName = getFileName(response);
                    fileDir = fileDir + fileName;
                    file = new File(fileDir);
                    //6.1.2根据文件路径获取输出流
                    FileOutputStream output = new FileOutputStream(file);

                    //6.2 输入流：从钉钉服务器返回的文件流，得到网络资源并写入文件
                    InputStream input = entity.getContent();

                    //6.3 将数据写入文件：将输入流中的数据写入到输出流
                    byte b[] = new byte[1024];
                    int j = 0;
                    while ((j = input.read(b)) != -1) {
                        output.write(b, 0, j);
                    }
                    output.flush();
                    output.close();
                }
                if (entity != null) {
                    entity.consumeContent();
                }
            }
        } catch (IOException e) {
            log.error("request url={}, exception, msg={}", url, e.getMessage());
            e.printStackTrace();
        } finally {
            if (response != null)
                try {
                    response.close(); //释放资源

                } catch (IOException e) {
                    e.printStackTrace();
                }
        }

        return file;
    }

    /**
     * @param url     请求url
     * @param data    post请求参数
     * @param fileDir 文件下载路径
     * @return
     * @throws Exception File
     * @desc ： 5.下载文件 - post
     */
    public static File downloadMedia(String url, Object data, String fileDir) throws Exception {
        //1.生成一个请求
        HttpPost httpPost = new HttpPost(url);

        //2.配置请求属性
        //2.1 设置请求超时时间
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(100000).setConnectTimeout(100000).build();
        httpPost.setConfig(requestConfig);
        //2.2 设置数据传输格式-json
        httpPost.addHeader("Content-Type", "application/json");
        //2.3 设置请求参数
        StringEntity requestEntity = new StringEntity(JSON.toJSONString(data), "utf-8");
        httpPost.setEntity(requestEntity);

        //3.发起请求，获取响应信息
        //3.1 创建httpClient
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;

        //4.设置本地保存的文件
        //File file = new File(fileDir);
        File file = null;
        try {
            //5. 发起请求，获取响应信息
            response = httpClient.execute(httpPost, new BasicHttpContext());
            log.info("HttpStatus.SC_OK:{}", HttpStatus.SC_OK);
            log.info("response.getStatusLine().getStatusCode():{}", response.getStatusLine().getStatusCode());
            log.info("http-header:{}", JSON.toJSONString(response.getAllHeaders()));
            log.info("http-filename:{}", getFileName(response));

            //请求成功
            if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode()) {

                //6.取得请求内容
                HttpEntity entity = response.getEntity();

                if (entity != null) {
                    //这里可以得到文件的类型 如image/jpg /zip /tiff 等等 但是发现并不是十分有效，有时明明后缀是.rar但是取到的是null，这点特别说明
                    //System.out.println(entity.getContentType());
                    //可以判断是否是文件数据流
                    log.info("entity.isStreaming()={}", entity.isStreaming());

                    //6.1 输出流
                    //6.1.1获取文件名，拼接文件路径
                    String fileName = getFileName(response);
                    fileDir = fileDir + fileName;
                    file = new File(fileDir);
                    //6.1.2根据文件路径获取输出流
                    FileOutputStream output = new FileOutputStream(file);

                    //6.2 输入流：从钉钉服务器返回的文件流，得到网络资源并写入文件
                    InputStream input = entity.getContent();

                    //6.3 将数据写入文件：将输入流中的数据写入到输出流
                    byte b[] = new byte[1024];
                    int j = 0;
                    while ((j = input.read(b)) != -1) {
                        output.write(b, 0, j);
                    }
                    output.flush();
                    output.close();
                }
                if (entity != null) {
                    entity.consumeContent();
                }
            }
        } catch (IOException e) {
            log.error("request url={}, exception, msg={}", url, e.getMessage());
            e.printStackTrace();
        } finally {
            if (response != null)
                try {
                    response.close(); //释放资源

                } catch (IOException e) {
                    e.printStackTrace();
                }
        }

        return file;
    }

    /**
     * 5. 获取response header中Content-Disposition中的filename值
     *
     * @param response 响应
     * @return String
     * @desc ：
     */
    public static String getFileName(HttpResponse response) {
        Header contentHeader = response.getFirstHeader("Content-Disposition");
        String filename = null;
        if (contentHeader != null) {
            HeaderElement[] values = contentHeader.getElements();
            if (values.length == 1) {
                NameValuePair param = values[0].getParameterByName("filename");
                if (param != null) {
                    try {
                        //filename = new String(param.getValue().toString().getBytes(), "utf-8");
                        //filename=URLDecoder.decode(param.getValue(),"utf-8");
                        filename = param.getValue();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return filename;
    }

    public static String executePOSTRequest(String url, MultiValueMap<String, String> params) {
        String res;
        try {
            SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
            // 设置超时时间为5s
            requestFactory.setConnectTimeout(5000);
            requestFactory.setReadTimeout(5000);
            RestTemplate restTemplate = new RestTemplate(requestFactory);
            org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
            org.springframework.http.HttpEntity<MultiValueMap<String, String>> httpEntity = new org.springframework.http.HttpEntity<>(params, headers);
            log.debug("httpEntity={}", httpEntity);
            log.debug("url={}", url);
            res = restTemplate.postForObject(url, httpEntity, String.class);
            log.debug("res={}", res.length() > 1000 ? res.substring(0, 500) : res);
        } catch (Exception e) {
            log.error("request fail,e:", e);
            throw new BusinessException("未知错误");
        }
        return res;
    }

    public static String executeGETRequest(String url, Map<String, Object> params) {
        String res;
        try {
            SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
            // 设置超时时间为5s
            requestFactory.setConnectTimeout(0);
            requestFactory.setReadTimeout(0);
            RestTemplate restTemplate = new RestTemplate(requestFactory);
            log.debug("param={}", params);
            if (!CollectionUtils.isEmpty(params)) {
                url += String.format("?%s", params.entrySet().stream().map(entry -> entry.getKey() + "=" + entry.getValue()).collect(Collectors.joining("&")));
            }
            log.debug("url={}", url);
            res = restTemplate.getForObject(url, String.class);
            log.debug("res={}", res.length() > 1000 ? res.substring(0, 500) : res);
        } catch (Exception e) {
            log.error("request fail,e:", e);
            throw new BusinessException("未知错误");
        }
        return res;
    }
}